package io.github.fb513.api.facade;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.github.fb513.api.entity.Friendship;
import io.github.fb513.api.entity.FriendshipLog;
import io.github.fb513.api.entity.User;
import io.github.fb513.api.entity.UserOnline;
import io.github.fb513.api.enums.ActiveUserStatusEnum;
import io.github.fb513.api.enums.FriendStatusEnum;
import io.github.fb513.api.enums.RespMetaEnum;
import io.github.fb513.api.exception.WebBaseException;
import io.github.fb513.api.model.request.FriendshipReq;
import io.github.fb513.api.model.request.RemarkReq;
import io.github.fb513.api.model.response.FriendshipResp;
import io.github.fb513.api.queue.MessageSender;
import io.github.fb513.api.service.*;
import io.github.fb513.api.util.FutureUtils;
import io.github.fb513.common.enums.CommandEnum;
import io.github.fb513.common.model.BaseData;
import io.github.fb513.common.model.FriendshipData;
import io.github.fb513.common.model.MessageInfo;
import io.github.fb513.common.util.ImUtils;
import io.github.fb513.common.util.JsonParser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author fengbo
 * @since 2023/12/30 16:37
 */
@Component
public class FriendshipFacade {

    @Autowired private IUserService userService;
    @Autowired private IFriendshipService friendshipService;
    @Autowired private IFriendshipLogService friendshipLogService;
    @Autowired private MessageSender messageSender;
    @Autowired private IUserOnlineService userOnlineService;
    @Autowired private JsonParser jsonParser;
    @Autowired private IUserMessageService userMessageService;
    @Autowired private ThreadPoolExecutor executor;

    public FriendshipResp addFriend(FriendshipReq req) {
        List<User> userList = userService.list(new LambdaQueryWrapper<User>()
                .select(User::getId, User::getNickName, User::getUserSex)
                .in(User::getId, req.getSourceId(), req.getTargetId()));
        if (userList.size() != 2) {
            throw new WebBaseException(RespMetaEnum.NO_USER);
        }
        FriendshipLog dataLog = new FriendshipLog();
        dataLog.setOwnerId(req.getSourceId());
        dataLog.setFriendId(req.getTargetId());
        Friendship friendship = friendshipService.getOne(new LambdaQueryWrapper<Friendship>()
                .select(Friendship::getFriendStatus)
                .eq(Friendship::getOwnerId, req.getSourceId())
                .eq(Friendship::getFriendId, req.getTargetId()));
        if (friendship == null) {
            friendship = new Friendship();
            friendship.setOwnerId(req.getSourceId());
            friendship.setFriendId(req.getTargetId());
            friendship.setFriendStatus(FriendStatusEnum.NORMAL.getCode());
            friendshipLogService.save(dataLog);
            friendshipService.save(friendship);
        } else {
            if (FriendStatusEnum.NORMAL.getCode().equals(friendship.getFriendStatus())) {
                throw new WebBaseException(RespMetaEnum.FRIEND_ALREADY);
            }
            dataLog.setColumnName("friend_status");
            dataLog.setBeforeValue(String.valueOf(friendship.getFriendStatus()));
            dataLog.setAfterValue(String.valueOf(FriendStatusEnum.NORMAL.getCode()));
            friendshipLogService.save(dataLog);
            friendshipService.update(new LambdaUpdateWrapper<Friendship>()
                    .set(Friendship::getFriendStatus, FriendStatusEnum.NORMAL.getCode())
                    .eq(Friendship::getOwnerId, req.getSourceId())
                    .eq(Friendship::getFriendId, req.getTargetId()));
        }

        // 发送消息
        Map<Long, User> userMap = userList.stream().collect(Collectors.toMap(User::getId, v -> v));
        List<UserOnline> activeUserList = userOnlineService.list(new LambdaQueryWrapper<UserOnline>()
                .select(UserOnline::getId, UserOnline::getUserId, UserOnline::getClientType, UserOnline::getBrokerHost, UserOnline::getBrokerPort)
                .in(UserOnline::getUserId, userMap.keySet())
                .eq(UserOnline::getActiveStatus, ActiveUserStatusEnum.ONLINE.getCode())
        );
        Map<Long, List<UserOnline>> activeUserGroup = activeUserList.stream().collect(Collectors.groupingBy(UserOnline::getUserId));
        // 发送给自己
        User sourceUser = userMap.get(req.getSourceId());
        User targetUser = userMap.get(req.getTargetId());
        List<UserOnline> sourceActiveList = activeUserGroup.get(req.getSourceId());
        if (!CollectionUtils.isEmpty(sourceActiveList)) {
            for (UserOnline activeUser : sourceActiveList) {
                if (req.getActiveUserId().equals(activeUser.getId())) {
                    continue;
                }
                messageSender.sendAsync(ImUtils.getBrokerKey(activeUser.getBrokerHost(), activeUser.getBrokerPort()),
                        jsonParser.formatObjToJson(getFriendshipData(CommandEnum.FRIENDSHIP_ACK.getCommand(), activeUser, targetUser)));
            }
        }

        Friendship targetFriendship = friendshipService.getOne(new LambdaQueryWrapper<Friendship>()
                .select(Friendship::getFriendStatus)
                .eq(Friendship::getOwnerId, req.getSourceId())
                .eq(Friendship::getFriendId, req.getTargetId())
                .eq(Friendship::getFriendStatus, FriendStatusEnum.NORMAL.getCode())
        );
        FriendshipResp resp = new FriendshipResp();
        resp.setUserId(targetUser.getId());
        resp.setNickName(targetUser.getNickName());
        resp.setUserSex(targetUser.getUserSex());
        if (targetFriendship != null) {
            return resp;
        }
        // 发送给对方
        List<UserOnline> targetActiveList = activeUserGroup.get(req.getTargetId());
        if (!CollectionUtils.isEmpty(targetActiveList)) {
            for (UserOnline activeUser : sourceActiveList) {
                messageSender.sendAsync(ImUtils.getBrokerKey(activeUser.getBrokerHost(), activeUser.getBrokerPort()),
                        jsonParser.formatObjToJson(getFriendshipData(CommandEnum.FRIENDSHIP.getCommand(), activeUser, sourceUser)));
            }
        }
        return resp;
    }

    private MessageInfo<BaseData<FriendshipData>> getFriendshipData(int command, UserOnline activeUser, User user) {
        FriendshipData friendshipData = new FriendshipData();
        friendshipData.setFriendId(user.getId());
        friendshipData.setNickName(user.getNickName());
        friendshipData.setUserSex(user.getUserSex());
        return MessageInfo.getJsonMessage(command, new BaseData<>(activeUser.getUserId(), activeUser.getClientType(), friendshipData));
    }

    public void remarkFriend(RemarkReq req) {
        Friendship friendship = friendshipService.getOne(new LambdaQueryWrapper<Friendship>()
                .select(Friendship::getRemark)
                .eq(Friendship::getOwnerId, req.getOwnerId())
                .eq(Friendship::getFriendId, req.getFriendId())
                .in(Friendship::getFriendStatus, FriendStatusEnum.NORMAL.getCode(), FriendStatusEnum.BLACKLIST.getCode())
        );
        if (friendship == null) {
            throw new WebBaseException(RespMetaEnum.FRIEND_DENY);
        }
        FriendshipLog dataLog = new FriendshipLog();
        dataLog.setOwnerId(req.getOwnerId());
        dataLog.setFriendId(req.getFriendId());
        dataLog.setColumnName("remark");
        dataLog.setBeforeValue(friendship.getRemark());
        dataLog.setAfterValue(req.getRemark());
        friendshipLogService.save(dataLog);
        friendshipService.update(new LambdaUpdateWrapper<Friendship>()
               .set(Friendship::getRemark, req.getRemark())
               .eq(Friendship::getOwnerId, req.getOwnerId())
               .eq(Friendship::getFriendId, req.getFriendId()));
    }

    public List<FriendshipResp> offlineFriend(Long userId, Long maxSeq) {

        List<Friendship> friendList = friendshipService.list(new LambdaQueryWrapper<Friendship>()
                .select(Friendship::getFriendId, Friendship::getRemark, Friendship::getMaxMessageId, Friendship::getReadMessageId)
                .eq(Friendship::getOwnerId, userId)
                .eq(Friendship::getFriendStatus, FriendStatusEnum.NORMAL.getCode())
        );
        if (CollectionUtils.isEmpty(friendList)) {
            return Collections.emptyList();
        }
        List<Long> userIds = friendList.stream().map(Friendship::getFriendId).toList();
        Map<Long, User> userMap = userService.list(new LambdaQueryWrapper<User>()
                        .select(User::getId, User::getNickName, User::getUserSex, User::getAvatarUrl)
                        .in(User::getId, userIds))
                .stream().collect(Collectors.toMap(User::getId, Function.identity()));
        List<FriendshipResp> friendRespList = new ArrayList<>(friendList.size());
        List<CompletableFuture<?>> futureList = new ArrayList<>();
        for (Friendship friendship : friendList) {
            User friend = userMap.get(friendship.getFriendId());
            FriendshipResp friendResp = new FriendshipResp();
            friendResp.setUserId(friend.getId());
            if (StringUtils.hasText(friendship.getRemark())) {
                friendResp.setNickName(friendship.getRemark());
            } else {
                friendResp.setNickName(friend.getNickName());
            }
            friendResp.setAvatarUrl(friend.getAvatarUrl());
            if (friendship.getReadMessageId() < friendship.getMaxMessageId()) {
                CompletableFuture<Void> future = CompletableFuture.runAsync(() -> friendResp.setMessageList(
                        userMessageService.getMessageList(userId, friend.getId(), null, friendship.getReadMessageId())), executor);
                futureList.add(future);
            }
            friendRespList.add(friendResp);
        }
        if (!futureList.isEmpty()) {
            FutureUtils.waitAllTasks(futureList);
        }
        return friendRespList;
    }
}
